import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil'
import { getBusinessObject, is } from 'bpmn-js/lib/util/ModelUtil'
import { ModdleElement } from 'bpmn-moddle'
import { find } from 'min-dash'
import { getExtensionElementsList } from './extensionElements'
import { useSettingStore } from '@/store/bpmnProcess/settingStore'
import { useModelerStore } from '@/store/bpmnProcess/modelerStore'

// 任务相关实现方式选择外部后，外部错误
export const isExternalErrorsSupported = (element) => {
  return is(element, 'bpmn:ServiceTask') && getImplementationType(element) === 'external'
}

// 错误结束事件
export function isErrorSupported(element): boolean {
  return isAny(element, ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:EndEvent']) && !!getErrorEventDefinition(element)
}

export function getErrorEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:ErrorEventDefinition')
}

export function getError(element) {
  const errorEventDefinition = getErrorEventDefinition(element)

  return errorEventDefinition && errorEventDefinition.get('errorRef')
}

export function getCurrentError(element, value) {
  const businessObject = getBusinessObject(element)
  return findRootElementById(businessObject, 'bpmn:Error', value)
}

// 消息事件
export function isMessageSupported(element): boolean {
  return is(element, 'bpmn:ReceiveTask') || (isAny(element, ['bpmn:StartEvent', 'bpmn:EndEvent', 'bpmn:IntermediateThrowEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent']) && !!getMessageEventDefinition(element))
}

export function getMessageEventDefinition(element): ModdleElement | undefined {
  if (is(element, 'bpmn:ReceiveTask')) {
    return getBusinessObject(element)
  }

  return getEventDefinition(element, 'bpmn:MessageEventDefinition')
}

export function getMessage(element): ModdleElement | undefined {
  const messageEventDefinition = getMessageEventDefinition(element)

  return messageEventDefinition && messageEventDefinition.get('messageRef')
}

// 信号事件
export function isSignalSupported(element): boolean {
  return is(element, 'bpmn:Event') && !!getSignalEventDefinition(element)
}

export function getSignalEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:SignalEventDefinition')
}

export function getSignal(element): ModdleElement | undefined {
  const signalEventDefinition = getSignalEventDefinition(element)

  return signalEventDefinition && signalEventDefinition.get('signalRef')
}

// 上报事件
export function isEscalationSupported(element): boolean {
  return is(element, 'bpmn:Event') && !!getEscalationEventDefinition(element)
}

export function getEscalationEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:EscalationEventDefinition')
}

export function getEscalation(element): ModdleElement | undefined {
  const escalationEventDefinition = getEscalationEventDefinition(element)

  return escalationEventDefinition && escalationEventDefinition.get('escalationRef')
}

// 更新事件数据
export function updateEventDefinition(element, type, props) {
  const modeling = useModelerStore().getModeling
  const eventDefinition = getCurrentObject(element, type)
  if (props) {
    const result = findRootElementById(eventDefinition, type, props.id)
    result.name = props.name
    if (type === 'bpmn:Error') {
      result.errorCode = props.errorCode
      result.errorMessage = props.errorMessage
    }
    modeling.updateModdleProperties(element, eventDefinition, { [getEventDefinitionField(type)]: result })
  } else {
    modeling.updateModdleProperties(element, eventDefinition, { [getEventDefinitionField(type)]: undefined })
  }
}

function getEventDefinitionField(type) {
  if (type === 'bpmn:Error') {
    return 'errorRef'
  } else if (type === 'bpmn:Message') {
    return 'messageRef'
  } else if (type === 'bpmn:Signal') {
    return 'signalRef'
  } else if (type === 'bpmn:Escalation') {
    return 'escalationRef'
  }
}

// 定时器事件
export function isTimerSupported(element): boolean {
  return isAny(element, ['bpmn:StartEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:BoundaryEvent']) && !!getTimerEventDefinition(element)
}

/**
 * Get the timer definition type for a given timer event definition.
 *
 * @param {ModdleElement<bpmn:TimerEventDefinition>} timer
 *
 * @return {string|undefined} the timer definition type
 */
export function getTimerDefinitionType(timer: ModdleElement): string | undefined {
  if (!timer) {
    return ''
  }

  const timeDate = timer.get('timeDate')
  if (typeof timeDate !== 'undefined') {
    return 'timeDate'
  }

  const timeCycle = timer.get('timeCycle')
  if (typeof timeCycle !== 'undefined') {
    return 'timeCycle'
  }

  const timeDuration = timer.get('timeDuration')
  if (typeof timeDuration !== 'undefined') {
    return 'timeDuration'
  }
  return ''
}

export function getTimerEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:TimerEventDefinition')
}

// 链接事件
export function isLinkSupported(element): boolean {
  return isAny(element, ['bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent']) && !!getLinkEventDefinition(element)
}

export function getLinkEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:LinkEventDefinition')
}

// 补偿事件
export function isCompensationSupported(element): boolean {
  return isAny(element, ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent']) && !!getCompensateEventDefinition(element)
}

export function getCompensateEventDefinition(element): ModdleElement | undefined {
  return getEventDefinition(element, 'bpmn:CompensateEventDefinition')
}

export function getCompensateActivity(element): ModdleElement | undefined {
  const compensateEventDefinition = getCompensateEventDefinition(element)

  return compensateEventDefinition && compensateEventDefinition.get('activityRef')
}

// helper
export function getEventDefinition(element, eventType: string): ModdleElement | undefined {
  const businessObject = getBusinessObject(element)

  const eventDefinitions = businessObject.get('eventDefinitions') || []

  return find(eventDefinitions, function (definition) {
    return is(definition, eventType)
  })
}

export function getRoot(businessObject) {
  let parent = businessObject
  while (parent.$parent) {
    parent = parent.$parent
  }
  return parent
}

export function getCurrentObject(element, type) {
  if (type === 'bpmn:Error') {
    if (isExternalErrorsSupported(element)) {
      return getBusinessObject(element)
    } else {
      return getErrorEventDefinition(element)
    }
  } else if (type === 'bpmn:Message') {
    return getMessageEventDefinition(element)
  } else if (type === 'bpmn:Signal') {
    return getSignalEventDefinition(element)
  } else if (type === 'bpmn:Escalation') {
    return getEscalationEventDefinition(element)
  }
}

function filterElementsByType(objectList, type) {
  const list = objectList || []
  return list.filter((element) => is(element, type))
}

export function findRootElementsByType(businessObject, referencedType) {
  const root = getRoot(businessObject)
  return filterElementsByType(root.get('rootElements'), referencedType)
}

export function findRootElementById(businessObject, type, id) {
  const elements = findRootElementsByType(businessObject, type)
  return elements.find((element) => element.id === id)
}

type ImplementationType = 'dmn' | 'connector' | 'external' | 'class' | 'expression' | 'delegateExpression' | 'script' | undefined
// ///////////////////////////////////////// bpmn 根据流程引擎的扩展方法

// Check whether an element is ServiceTaskLike 检查元素是否为 'ServiceTaskLike'
export function isServiceTaskLike(element): boolean {
  const prefix = useSettingStore().editor.processEngine
  return is(element, `${prefix}:ServiceTaskLike`)
}

// Returns 'true' if the given element is 'DmnCapable'
export function isDmnCapable(element): boolean {
  const prefix = useSettingStore().editor.processEngine
  return is(element, `${prefix}:DmnCapable`)
}

// Returns 'true' if the given element is 'ExternalCapable'
export function isExternalCapable(element): boolean {
  const prefix = useSettingStore().editor.processEngine
  return is(element, `${prefix}:ExternalCapable`)
}

/**
 * getServiceTaskLikeBusinessObject
 * 获取一个 'ServiceTaskLike' 业务对象。
 * 如果给定的元素不是 'servicetasklike '，则返回 'false'
 */
export function getServiceTaskLikeBusinessObject(element): ModdleElement | false {
  if (is(element, 'bpmn:IntermediateThrowEvent') || is(element, 'bpmn:EndEvent')) {
    const messageEventDefinition = getMessageEventDefinition(element)
    if (messageEventDefinition) {
      element = messageEventDefinition
    }
  }
  return isServiceTaskLike(element) && getBusinessObject(element)
}

/**
 * 返回给定元素的实现类型。
 * 可能的实现类型有:
 * - dmn
 * - connector
 * - external
 * - class
 * - expression
 * - delegateExpression
 * - script
 * - or undefined, when no matching implementation type is found
 */
export function getImplementationType(element): ImplementationType {
  const prefix = useSettingStore().editor.processEngine
  const businessObject = getListenerBusinessObject(element) || getServiceTaskLikeBusinessObject(element)

  if (!businessObject) {
    return
  }

  if (isDmnCapable(businessObject)) {
    const decisionRef = businessObject.get(`${prefix}:decisionRef`)
    if (typeof decisionRef !== 'undefined') {
      return 'dmn'
    }
  }

  if (isServiceTaskLike(businessObject)) {
    const connectors = getExtensionElementsList(businessObject, `${prefix}:Connector`)
    if (connectors.length) {
      return 'connector'
    }
  }

  if (isExternalCapable(businessObject)) {
    const type = businessObject.get(`${prefix}:type`)
    if (type === 'external') {
      return 'external'
    }
  }

  const cls = businessObject.get(`${prefix}:class`)
  if (typeof cls !== 'undefined') {
    return 'class'
  }

  const expression = businessObject.get(`${prefix}:expression`)
  if (typeof expression !== 'undefined') {
    return 'expression'
  }

  const delegateExpression = businessObject.get(`${prefix}:delegateExpression`)
  if (typeof delegateExpression !== 'undefined') {
    return 'delegateExpression'
  }

  const script = businessObject.get('script')
  if (typeof script !== 'undefined') {
    return 'script'
  }
}

function getListenerBusinessObject(businessObject): ModdleElement | undefined {
  const prefix = useSettingStore().editor.processEngine
  if (isAny(businessObject, [`${prefix}:ExecutionListener`, `${prefix}:TaskListener`])) {
    return businessObject as ModdleElement
  }
}

export function getConnectors(businessObject) {
  const prefix = useSettingStore().editor.processEngine
  return getExtensionElementsList(businessObject, `${prefix}:Connector`)
}

export function getConnector(element) {
  const businessObject = getServiceTaskLikeBusinessObject(element)
  const connectors = getConnectors(businessObject)
  return connectors[0]
}
